package com.ruoyi.search.manager;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.exception.base.BaseException;
import com.ruoyi.search.bo.EsOperLog;
import com.ruoyi.search.dto.EsOperLogDto;
import com.ruoyi.search.dto.EsPageDTO;
import com.ruoyi.search.vo.EsPageVO;
import com.ruoyi.search.constant.EsIndexEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Component
@Slf4j
public class OperLogManager {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    public EsPageVO<EsOperLog> pageSearchResult(EsPageDTO esPageDTO, EsOperLogDto esOperLogDto) {
        //1、动态构建出查询需要的DSL语句
        EsPageVO<EsOperLog> result;
        //1、准备检索请求
        SearchRequest searchRequest = buildSearchRequest(esPageDTO, esOperLogDto);
        try {
            //2、执行检索请求
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            log.debug("搜索返回结果：" + response.toString());

            //3、分析响应数据，封装成我们需要的格式
            result = buildSearchResult(esPageDTO, response);
        } catch (Exception ex) {
            log.error(ex.toString());
            throw new BaseException("搜索服务出了点小差，请稍后再试", String.valueOf(ex));
        }
        return result;
    }

    private EsPageVO<EsOperLog> buildSearchResult(EsPageDTO pageDTO, SearchResponse response) {
        EsPageVO<EsOperLog> esPageVO = new EsPageVO<>();

        //1、返回的所有查询到的商品
        SearchHits hits = response.getHits();
        List<EsOperLog> productSearchs = getEsOrderBOList(response);
        esPageVO.setList(productSearchs);


        //===============分页信息====================//
        //总记录数
        long total = hits.getTotalHits().value;
        esPageVO.setTotal(total);
        // 总页码
        int totalPages = (int) total % pageDTO.getPageSize() == 0 ?
                (int) total / pageDTO.getPageSize() : ((int) total / pageDTO.getPageSize() + 1);
        esPageVO.setPages(totalPages);
        return esPageVO;
    }

    private List<EsOperLog> getEsOrderBOList(SearchResponse response) {

        return getOrderListByResponse(response.getHits().getHits());
    }

    /**
     * 从es返回的数据中获取spu列表
     *
     * @param hits es返回的数据
     * @return
     */
    private List<EsOperLog> getOrderListByResponse(SearchHit[] hits) {
        List<EsOperLog> esOperLogs = new ArrayList<>();
        for (SearchHit hit : hits) {
            EsOperLog esOrder = JSONObject.parseObject(hit.getSourceAsString(), EsOperLog.class);
            esOperLogs.add(esOrder);
        }
        return esOperLogs;
    }

    private SearchRequest buildSearchRequest(EsPageDTO pageDTO, EsOperLogDto param) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 过滤
        filterQueryIfNecessary(param, boolQueryBuilder);

        // 关键字搜索
        keywordSearch(param, boolQueryBuilder);

        // 排序
        sort(searchSourceBuilder, boolQueryBuilder);

        //分页
        if (Objects.nonNull(pageDTO)) {
            searchSourceBuilder.from((pageDTO.getPageNum() - 1) * pageDTO.getPageSize());
            searchSourceBuilder.size(pageDTO.getPageSize());
        }

        log.debug("构建的DSL语句 {}", searchSourceBuilder.toString());

        return new SearchRequest(new String[]{EsIndexEnum.OPERLOG.value()}, searchSourceBuilder);
    }

    private void keywordSearch(EsOperLogDto param, BoolQueryBuilder boolQueryBuilder) {
        // 创建查询语句 ES中must和should不能同时使用 同时使用should失效 嵌套多个must 将should条件拼接在一个must中即可


        BoolQueryBuilder keywordShouldQuery = QueryBuilders.boolQuery();

        // 店铺名称
        if (StringUtils.isNotBlank(param.getOperName())) {
            keywordShouldQuery.should(QueryBuilders.matchQuery("operName", param.getOperName()));
        }

        boolQueryBuilder.must(keywordShouldQuery);
    }

    /**
     * 进行排序
     */
    private void sort(SearchSourceBuilder searchSourceBuilder, BoolQueryBuilder boolQueryBuilder) {
        searchSourceBuilder.sort("operTime", SortOrder.DESC);
        searchSourceBuilder.query(boolQueryBuilder);
    }

    private void filterQueryIfNecessary(EsOperLogDto param, BoolQueryBuilder boolQueryBuilder) {
        /** 操作模块 */
        if (Objects.nonNull(param.getTitle())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("title", param.getTitle()));
        }
        /** 业务类型（0其它 1新增 2修改 3删除） */
        if (Objects.nonNull(param.getBusinessType())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("businessType", param.getTitle()));
        }

        // 开始时间 - 结束时间
        if (param.getStartTime() != null || param.getEndTime() != null) {
            // 销售价
            String createTime = "operTime";
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(createTime);
            if (param.getStartTime() != null) {
                rangeQueryBuilder.gte(param.getStartTime());
            }
            if (param.getEndTime() != null) {
                rangeQueryBuilder.lte(param.getEndTime());
            }
            boolQueryBuilder.filter(rangeQueryBuilder);
        }
    }
}
